1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.apache.tapestry5.internal.bindings;
16
17 import org.apache.tapestry5.Binding;
18 import org.apache.tapestry5.ComponentResources;
19 import org.apache.tapestry5.annotations.BeforeRenderBody;
20 import org.apache.tapestry5.beaneditor.Validate;
21 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
22 import org.apache.tapestry5.internal.util.IntegerRange;
23 import org.apache.tapestry5.ioc.Location;
24 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
25 import org.apache.tapestry5.ioc.internal.util.TapestryException;
26 import org.apache.tapestry5.runtime.Component;
27 import org.apache.tapestry5.services.BindingFactory;
28 import org.testng.annotations.AfterClass;
29 import org.testng.annotations.BeforeClass;
30 import org.testng.annotations.DataProvider;
31 import org.testng.annotations.Test;
32
33 public class PropBindingFactoryTest extends InternalBaseTestCase
34 {
35 private BindingFactory factory;
36
37 @BeforeClass
38 public void setup_factory()
39 {
40 factory = getService("PropBindingFactory", BindingFactory.class);
41 }
42
43 @AfterClass
44 public void cleanup_factory()
45 {
46 factory = null;
47 }
48
49 private ComponentResources newComponentResources(Component component)
50 {
51 ComponentResources resources = mockComponentResources();
52 train_getComponent(resources, component);
53
54 train_getCompleteId(resources, "foo.Bar:baz");
55
56 return resources;
57 }
58
59 @Test
60 public void object_property()
61 {
62 TargetBean bean = new TargetBean();
63 ComponentResources resources = newComponentResources(bean);
64 Location l = mockLocation();
65
66 replay();
67
68 Binding binding = factory.newBinding("test binding", resources, null, "objectValue", l);
69
70 assertSame(binding.getBindingType(), String.class);
71
72 bean.setObjectValue("first");
73
74 assertEquals(binding.get(), "first");
75
76 binding.set("second");
77
78 assertEquals(bean.getObjectValue(), "second");
79 assertEquals(InternalUtils.locationOf(binding), l);
80
81 assertEquals(binding.toString(), "PropBinding[test binding foo.Bar:baz(objectValue)]");
82
83 verify();
84 }
85
86 @Test
87 public void annotation_from_read_only_property()
88 {
89 TargetBean bean = new TargetBean();
90 ComponentResources resources = newComponentResources(bean);
91 Location l = mockLocation();
92
93 replay();
94
95 Binding binding = factory.newBinding("test binding", resources, null, "readOnly", l);
96
97 assertEquals(binding.getAnnotation(Validate.class).value(), "readonly");
98
99 verify();
100 }
101
102 @Test
103 public void annotation_from_write_only_property()
104 {
105 TargetBean bean = new TargetBean();
106 ComponentResources resources = newComponentResources(bean);
107 Location l = mockLocation();
108
109 replay();
110
111 Binding binding = factory.newBinding("test binding", resources, null, "writeOnly", l);
112
113 assertEquals(binding.getAnnotation(Validate.class).value(), "writeonly");
114
115 verify();
116 }
117
118 @Test
119 public void annotation_does_not_exist()
120 {
121 TargetBean bean = new TargetBean();
122 ComponentResources resources = newComponentResources(bean);
123 Location l = mockLocation();
124
125 replay();
126
127 Binding binding = factory.newBinding("test binding", resources, null, "intValue", l);
128
129 assertNull(binding.getAnnotation(Validate.class));
130
131 verify();
132 }
133
134 @Test
135 public void annotation_on_named_method()
136 {
137 TargetBean bean = new TargetBean();
138 ComponentResources resources = newComponentResources(bean);
139 Location l = mockLocation();
140
141 replay();
142
143 Binding binding = factory.newBinding("test binding", resources, null, "stringHolderMethod()", l);
144
145 assertNotNull(binding.getAnnotation(BeforeRenderBody.class));
146
147 verify();
148 }
149
150 @Test
151 public void annnotation_on_read_method_takes_precedence_over_write_method()
152 {
153 TargetBean bean = new TargetBean();
154 ComponentResources resources = newComponentResources(bean);
155 Location l = mockLocation();
156
157 replay();
158
159 Binding binding = factory.newBinding("test binding", resources, null, "objectValue", l);
160
161 assertEquals(binding.getAnnotation(Validate.class).value(), "getObjectValue");
162
163 verify();
164 }
165
166 @Test
167 public void property_path()
168 {
169 TargetBean bean = new TargetBean();
170 ComponentResources resources = newComponentResources(bean);
171 Location l = mockLocation();
172
173 replay();
174
175 Binding binding = factory.newBinding("test binding", resources, null, "stringHolder.value", l);
176
177 assertSame(binding.getBindingType(), String.class);
178
179 bean.getStringHolder().setValue("first");
180
181 assertEquals(binding.get(), "first");
182
183 binding.set("second");
184
185 assertEquals(bean.getStringHolder().getValue(), "second");
186
187 assertEquals(binding.toString(), "PropBinding[test binding foo.Bar:baz(stringHolder.value)]");
188
189 verify();
190 }
191
192
193
194
195 @Test
196 public void property_path_with_explicit_method_in_preamble()
197 {
198 TargetBean bean = new TargetBean();
199 ComponentResources resources = newComponentResources(bean);
200 Location l = mockLocation();
201
202 replay();
203
204 Binding binding = factory.newBinding("test binding", resources, null, "stringHolderMethod().value", l);
205
206 assertSame(binding.getBindingType(), String.class);
207
208 bean.getStringHolder().setValue("first");
209
210 assertEquals(binding.get(), "first");
211
212 assertEquals(binding.toString(), "PropBinding[test binding foo.Bar:baz(stringHolderMethod().value)]");
213
214 verify();
215 }
216
217 @Test
218 public void method_call_as_terminal()
219 {
220 TargetBean bean = new TargetBean();
221 ComponentResources resources = newComponentResources(bean);
222 Location l = mockLocation();
223
224 replay();
225
226 Binding binding = factory.newBinding("test binding", resources, null, "stringHolderMethod().stringValue()", l);
227
228 assertSame(binding.getBindingType(), String.class);
229
230 bean.getStringHolder().setValue("first");
231
232 assertEquals(binding.get(), "first");
233
234 try
235 {
236 binding.set("read-only");
237 unreachable();
238 }
239 catch (TapestryException ex)
240 {
241 assertEquals(
242 ex.getMessage(),
243 "Expression 'stringHolderMethod().stringValue()' for class org.apache.tapestry5.internal.bindings.TargetBean is read-only.");
244 assertSame(ex.getLocation(), l);
245 }
246
247 verify();
248 }
249
250 @Test
251 public void method_not_found_in_preamble()
252 {
253 TargetBean bean = new TargetBean();
254 ComponentResources resources = mockComponentResources();
255 Location l = mockLocation();
256
257 train_getComponent(resources, bean);
258
259 replay();
260
261 try
262 {
263 factory.newBinding("test binding", resources, null, "isThatRealBlood().value", l);
264 unreachable();
265 }
266 catch (RuntimeException ex)
267 {
268 assertMessageContains(ex,
269 "Class org.apache.tapestry5.internal.bindings.TargetBean does not contain a public method named 'isThatRealBlood()'");
270 }
271
272 verify();
273 }
274
275 @Test
276 public void method_not_found_in_terminal()
277 {
278 TargetBean bean = new TargetBean();
279 ComponentResources resources = mockComponentResources();
280 Location l = mockLocation();
281
282 train_getComponent(resources, bean);
283
284 replay();
285
286 try
287 {
288 factory.newBinding("test binding", resources, null, "stringHolder.isThatRealBlood()", l);
289 unreachable();
290 }
291 catch (RuntimeException ex)
292 {
293 assertMessageContains(ex, "StringHolder", "does not contain a public method", "isThatRealBlood()");
294 }
295
296 verify();
297 }
298
299 @Test
300 public void void_method_in_preamble()
301 {
302 TargetBean bean = new TargetBean();
303 ComponentResources resources = mockComponentResources();
304 Location l = mockLocation();
305
306 train_getComponent(resources, bean);
307
308 replay();
309
310 try
311 {
312 factory.newBinding("test binding", resources, null, "voidMethod().value", l);
313 unreachable();
314 }
315 catch (RuntimeException ex)
316 {
317 assertMessageContains(ex,
318 "Method org.apache.tapestry5.internal.bindings.TargetBean.voidMethod() returns void");
319 }
320
321 verify();
322 }
323
324 @Test
325 public void void_method_as_terminal()
326 {
327 TargetBean bean = new TargetBean();
328 ComponentResources resources = mockComponentResources();
329 Location l = mockLocation();
330
331 train_getComponent(resources, bean);
332
333 replay();
334
335 try
336 {
337 factory.newBinding("test binding", resources, null, "stringHolder.voidMethod()", l);
338 unreachable();
339 }
340 catch (RuntimeException ex)
341 {
342 assertMessageContains(ex,
343 "Method org.apache.tapestry5.internal.bindings.StringHolder.voidMethod() returns void");
344 }
345
346 verify();
347 }
348
349 @Test
350 public void property_path_through_missing_property()
351 {
352 TargetBean bean = new TargetBean();
353 ComponentResources resources = mockComponentResources();
354 Location l = mockLocation();
355
356 train_getComponent(resources, bean);
357
358 replay();
359
360 String propertyPath = "stringHolder.missingProperty.terminalProperty";
361
362 try
363 {
364 factory.newBinding("test binding", resources, null, propertyPath, l);
365 unreachable();
366 }
367 catch (RuntimeException ex)
368 {
369 assertMessageContains(ex,
370 "Class org.apache.tapestry5.internal.bindings.StringHolder does not contain a property",
371 "\'missingProperty\'");
372 }
373
374 verify();
375 }
376
377 @Test
378 public void property_path_through_write_only_property()
379 {
380 TargetBean bean = new TargetBean();
381 ComponentResources resources = mockComponentResources();
382 Location l = mockLocation();
383
384 train_getComponent(resources, bean);
385
386 replay();
387
388 String propertyPath = "writeOnly.terminalProperty";
389
390 try
391 {
392 factory.newBinding("test binding", resources, null, propertyPath, l);
393 unreachable();
394 }
395 catch (RuntimeException ex)
396 {
397 assertMessageContains(ex,
398 "Property \'writeOnly\' of class org.apache.tapestry5.internal.bindings.TargetBean",
399 "is not readable (it has no read accessor method).");
400 }
401
402 verify();
403 }
404
405 @Test
406 public void primitive_property()
407 {
408 TargetBean bean = new TargetBean();
409 ComponentResources resources = newComponentResources(bean);
410 Location l = mockLocation();
411
412 replay();
413
414 Binding binding = factory.newBinding("test binding", resources, null, "intValue", l);
415
416 assertSame(binding.getBindingType(), int.class);
417
418 bean.setIntValue(1);
419
420 assertEquals(binding.get(), 1);
421
422 binding.set(2);
423
424 assertEquals(bean.getIntValue(), 2);
425
426 verify();
427 }
428
429 @Test
430 public void read_only_property()
431 {
432 TargetBean bean = new TargetBean();
433 ComponentResources resources = newComponentResources(bean);
434 Location l = mockLocation();
435
436 replay();
437
438 Binding binding = factory.newBinding("test binding", resources, null, "readOnly", l);
439
440 assertEquals(binding.get(), "ReadOnly");
441
442 try
443 {
444 binding.set("fail");
445 unreachable();
446 }
447 catch (TapestryException ex)
448 {
449 assertEquals(ex.getMessage(),
450 "Expression 'readOnly' for class org.apache.tapestry5.internal.bindings.TargetBean is read-only.");
451 assertEquals(ex.getLocation(), l);
452 }
453
454 verify();
455 }
456
457 @Test
458 public void write_only_property()
459 {
460 TargetBean bean = new TargetBean();
461 ComponentResources resources = newComponentResources(bean);
462 Location l = mockLocation();
463
464 replay();
465
466 Binding binding = factory.newBinding("test binding", resources, null, "writeOnly", l);
467
468 binding.set("updated");
469
470 assertEquals(bean.writeOnly, "updated");
471
472 try
473 {
474 assertEquals(binding.get(), "ReadOnly");
475 unreachable();
476 }
477 catch (TapestryException ex)
478 {
479 assertEquals(ex.getMessage(),
480 "Expression 'writeOnly' for class org.apache.tapestry5.internal.bindings.TargetBean is write-only.");
481 assertEquals(ex.getLocation(), l);
482 }
483
484 verify();
485 }
486
487 @Test
488 public void unknown_property()
489 {
490 TargetBean bean = new TargetBean();
491 ComponentResources resources = mockComponentResources();
492 Location l = mockLocation();
493
494 train_getComponent(resources, bean);
495
496 replay();
497
498 try
499 {
500 factory.newBinding("test binding", resources, null, "missingProperty", l);
501 unreachable();
502 }
503 catch (RuntimeException ex)
504 {
505 assertMessageContains(ex,
506 "Class org.apache.tapestry5.internal.bindings.TargetBean does not contain a property",
507 "\'missingProperty\'");
508 }
509
510 verify();
511 }
512
513 @Test(dataProvider = "values")
514 public void special_prop_binding_values(String expression, Object expected)
515 {
516 Location l = mockLocation();
517 String description = "my description";
518 ComponentResources resources = mockComponentResources();
519 Component component = mockComponent();
520
521 train_getComponent(resources, component);
522 train_getCompleteId(resources, "Does.not.matter");
523
524 replay();
525
526 Binding binding = factory.newBinding(description, resources, null, expression, l);
527
528 assertEquals(binding.get(), expected);
529
530
531
532 assertTrue(binding.isInvariant());
533
534 verify();
535 }
536
537 @DataProvider
538 public Object[][] values()
539 {
540 return new Object[][]
541 {
542 { "true", true, },
543 { "True", true, },
544 { " true ", true, },
545 { "false", false },
546 { "null", null },
547 { "3", 3l },
548 { " 37 ", 37l },
549 { " -227", -227l },
550 { " 5.", 5d },
551 { " -100.", -100d },
552 { " -0.0 ", -0d },
553 { "+50", 50l },
554 { "+7..+20", new IntegerRange(7, 20) },
555 { "+5.5", 5.5d },
556 { "1..10", new IntegerRange(1, 10) },
557 { " -20 .. -30 ", new IntegerRange(-20, -30) },
558 { "0.", 0d },
559 { " 227.75", 227.75d },
560 { " -10123.67", -10123.67d },
561 { "'Hello World'", "Hello World" },
562 { " 'Whitespace Ignored' ", "Whitespace Ignored" },
563 { " ' Inside ' ", " Inside " } };
564 }
565 }